#ifndef __EPOLLSERVER_HPP__
#define __EPOLLSERVER_HPP__

#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include "Socket.hpp"
using namespace SocketModule;
/**
 * 每个epoll实例中包含两个核心数据结构
 * 1. RBTree
 * 2. 就绪链表
 * 3. epoll_ctl && epoll_wait需要传递epfd的原因是因为要通过这个
 */
class EpollServer
{
    // const static int size = sizeof(fd_set) * sizeof(void *);
    const static int size = 64;
    const static int defaultfd = -1;

public:
    EpollServer(uint16_t port)
        : _ptr(std::make_unique<TCPSocket>(port)),
          _isrunning(false),
          _epfd(defaultfd)
    {
        _ptr->BuildTCPSocketfd(port);

        // 1. 创建epoll实例
        _epfd = epoll_create(256); // 获取新的fd

        if (_epfd < 0)
        {
            LOG(FATAL) << "create epoll fd failed !";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(FATAL) << "create epoll fd success !";

        // 2. 添加关心事件
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _ptr->getFD();

        // 将_ptr->getFD()添加到epoll实例的RBTree中，添加关心事件
        int ret = epoll_ctl(_epfd, EPOLL_CTL_ADD, _ptr->getFD(), &ev);

        if (ret < 0)
        {
            LOG(FATAL) << "epoll_ctl add listenfd failed !";
            exit(EPOLL_CTL_ERR);
        }
        else
        {
            LOG(FATAL) << "epoll_ctl add listenfd success !";
        }
    }

    void Start()
    {
        if (_isrunning)
            return;
        if (!_isrunning)
            _isrunning = true;
        int timeout = -1;
        while (true)
        {
            // 阻塞读容易看出效果
            int n = epoll_wait(this->_epfd, _revent, size, timeout);
            if (n > 0)
            {
                LOG(INFO) << "有事件就绪了";
                Dispatcher(n);
                continue;
            }
            else if (n == 0)
            {
                LOG(INFO) << "time out";
                continue;
            }
            else
            {
                LOG(ERROR) << "参数错误";
                continue;
            }
        }
        _isrunning = false;
    }

    void Dispatcher(int num)
    {

        LOG(INFO) << "events ready... ";
        for (int i = 0; i < num; i++)
        {
            // 检查普通fd和监听fd
            uint32_t events = _revent[i].events;
            int fd = _revent[i].data.fd;

            // 监听fd
            if (events | EPOLLIN)
            {
                if (fd == _ptr->getFD())
                {
                    LOG(INFO) << "new link... ";
                    Accepter();
                }
                // 普通fd
                else
                {
                    Recver(fd);
                }
            }
        }
    }
    void Accepter()
    {
        InetAddr *client;
        int sockfd = _ptr->AcceptSocket(client);

        if (sockfd >= 0)
        {
            // 获取新连接成功了，但是不能直接read/recv，我们不知道sockfd是否有事件就绪
            // epoll最清楚sockfd未来是否会有事件就绪，将它放入fd数组
            LOG(INFO) << "get a new link,sockfd is " << sockfd << ",client :" << client->StringAddrIP();

            // 监听到新链接就加入
            // 创建epoll实例
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = sockfd;

            // 关心新的fd上的事件
            int n = epoll_ctl(_epfd, EPOLLIN, sockfd, &ev);

            if (n < 0)
            {
                LOG(FATAL) << "epoll_ctl add sockfd failed !";
                exit(EPOLL_CTL_ERR);
            }
            else
            {
                LOG(FATAL) << "epoll_ctl add sockfd success !";
            }
        }
    }

    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say@ " << buffer << std::endl;
        }
        else if (n == 0)
        {
            // 对方关闭了fd
            LOG(INFO) << "client quit...";

            // 必须先移除在关闭，此接口要求是一个有效的fd
            int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (ret)
            {
                LOG(INFO) << "remove fd success";
            }
            close(fd);
        }
        else
        {
            LOG(ERROR) << "recv error";
            int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (ret)
            {
                LOG(INFO) << "remove fd success";
            }
            close(fd);
        }
    }

    void Stop()
    {
        if (!_isrunning)
            return;
        _isrunning = false;
    }
    ~EpollServer()
    {
        _ptr->Close();
        if (_epfd)
            close(_epfd);
    }

private:
    std::unique_ptr<Socket> _ptr;
    bool _isrunning;
    // int _fd_array[size];
    // struct pollfd _fds[size];

    int _epfd;
    struct epoll_event _revent[size]; // 事件数组
};

#endif
